<!DOCTYPE html>
<html>
  <head>
    <meta charset='utf-8'>

    <link rel="stylesheet" type="text/css" 
      href="/assets/css/straybirds.css" media="screen" />
    <link rel="stylesheet" type="text/css" 
      href="/assets/css/pygments.css" media="screen" />

    <!-- MathJax Section Start -->

    <script type="text/javascript"
    src="http://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML">
    </script>
    <script>
        MathJax.Hub.Config({
              tex2jax: {
              skipTags: ['script', 'noscript', 'style', 'textarea', 'pre']
              }
        });
        MathJax.Hub.Queue(function() {
            var all = MathJax.Hub.getAllJax(), i;
            for(i=0; i < all.length; i += 1) {
                all[i].SourceElement().parentNode.className += ' has-jax';
            }
        });
    </script>

    <!-- MathJax Section End -->

    <!-- Google Analytics Start-->
    <script>
  (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
  (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
  m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
  })(window,document,'script','//www.google-analytics.com/analytics.js','ga');

  ga('create', 'UA-48100787-1', 'minixalpha.github.io');
  ga('send', 'pageview');

</script>

    <!-- Google Analytics End -->

    <title>使用JVMTI获取Java多线程程序指令执行次序</title>
  </head>

  <body>
    <div class="container">
      <header>
        <div class="container">
          <h1>
              <a href="/" title="Home Page"> 潇湘夜雨 </a>
          <span class="github-src">
            <a href ="https://github.com/minixalpha/minixalpha.github.io"
               target="_blank"
               title="Fork me on GitHub">
              <img src="/assets/images/GitHub-Mark-Light-32px.png" alt="">
            </a>
          </span>
          </h1>
        </div>
      </header>

      <aside id="left-side">
        <h2> 分类 </h2>
  <ul class="category-list">
      
            
                <li>
                <a href="/categories/计算机系统"> 计算机系统 (3) </a>
                </li>
            
      
            
                <li>
                <a href="/categories/java"> java (1) </a>
                </li>
            
      
            
                <li>
                <a href="/categories/技术"> 技术 (5) </a>
                </li>
            
      
            
                <li>
                <a href="/categories/工具"> 工具 (4) </a>
                </li>
            
      
            
                <li>
                <a href="/categories/科研"> 科研 (5) </a>
                </li>
            
      
            
                <li>
                <a href="/categories/生活"> 生活 (1) </a>
                </li>
            
      
            
                <li>
                <a href="/categories/思想"> 思想 (2) </a>
                </li>
            
      
            
                <li>
                <a href="/categories/c语言"> c语言 (4) </a>
                </li>
            
      
            
                <li>
                <a href="/categories/虚拟机"> 虚拟机 (1) </a>
                </li>
            
      
            
                <li>
                <a href="/categories/英语"> 英语 (8) </a>
                </li>
            
      
            
                <li>
                <a href="/categories/读书"> 读书 (1) </a>
                </li>
            
      
            
                <li>
                <a href="/categories/源代码阅读"> 源代码阅读 (10) </a>
                </li>
            
      
  </ul>

      </aside>

      <aside id="right-side">
        <h2> 归档 </h2>
  <ul class="archive-list">
    
    
    
        
        
        
        
            
            <li>
                <a href="/2014/05">
                    2014-05 (1)
                </a>
            </li>

        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
            
            <li>
                <a href="/2014/04">
                    2014-04 (3)
                </a>
            </li>

        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
            
            <li>
                <a href="/2014/03">
                    2014-03 (11)
                </a>
            </li>

        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
            
            <li>
                <a href="/2014/02">
                    2014-02 (6)
                </a>
            </li>

        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
            
            <li>
                <a href="/2014/01">
                    2014-01 (3)
                </a>
            </li>

        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
            
            <li>
                <a href="/2013/11">
                    2013-11 (10)
                </a>
            </li>

        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
            
            <li>
                <a href="/2013/10">
                    2013-10 (3)
                </a>
            </li>

        
        
    
  </ul>

      </aside>

      <article>

<h1>使用JVMTI获取Java多线程程序指令执行次序</h1>

<hr>

<p>在Java多线程程序中，由于线程调度，指令间的次序在每次运行时都可能不相同，有时候，我们需要得到指令次序，用来分析程序的行为。这样细粒度的底层行为用一般方法很难完成，我们需要借助 <a href="http://docs.oracle.com/javase/7/docs/platform/jvmti/jvmti.html">JVM Tool Interface</a>，即JVMTI，来帮助我们获取Java虚拟机执行时的信息。本文先介绍编写JVMTI程序的基本框架，然后介绍如何使用JVMTI来获取多线程程序中指令之间的次序。</p>

<h2>JVMTI简介</h2>

<p>JVMTI是用于编写开发与监视工具的编程接口，使用它可以检查并控制运行于Java虚拟机上的程序。使用它可以完成性能分析，调试，监视(monitoring)，线程分析，覆盖分析(coverage analysis)等工具。</p>

<p>使用JVMTI可以编写出一个agent。在运行Java程序时，指定这个agent，那么当虚拟机运行程序时，如果agent中指定的一些事件发生，虚拟机就会调用agent中相应的回调函数。JVMTI提供了一系列可以指定的事件，以及获取虚拟机中信息的函数接口。</p>

<h2>JVMTI基本编程方法</h2>

<h3>编写agent</h3>

<ul>
<li>头文件
agent程序中，需要包含 <code>jvmti.h</code>头文件，才能使用JVMTI中提供的接口。</li>
</ul>
<div class="highlight"><pre><code class="c language-c" data-lang="c"><span class="cp">#include &lt;jvmti.h&gt;</span>
</code></pre></div>
<ul>
<li><p>基本事件
和agent有关的两个基本事件是agent的启动与关闭，我们需要自己编写与启动与关闭相关的函数，这样，虚拟机才知道启动与关闭agent时，都需要做些什么。</p>

<p>与启动相关的函数有两个，如果你的agent在虚拟机处于<code>OnLoad</code>阶段时启动，会调用<code>Agent_OnLoad</code>函数，如果你的agent在虚拟机处于<code>Live</code>阶段时启动，会调用<code>Agent_OnAttach</code>函数。</p>

<p>我的理解是，如果你的agent想要全程监视一个程序的运行，就编写<code>Agent_OnLoad</code>，并在启动虚拟机时指定agent。如果你的agent想获取一个已经在运行的虚拟机中程序的信息，就编写<code>Agent_OnAttach</code>。</p>

<p>两个函数的原型如下：</p></li>
</ul>
<div class="highlight"><pre><code class="c language-c" data-lang="c"><span class="n">JNIEXPORT</span> <span class="n">jint</span> <span class="n">JNICALL</span> 
<span class="n">Agent_OnLoad</span><span class="p">(</span><span class="n">JavaVM</span> <span class="o">*</span><span class="n">vm</span><span class="p">,</span> <span class="kt">char</span> <span class="o">*</span><span class="n">options</span><span class="p">,</span> <span class="kt">void</span> <span class="o">*</span><span class="n">reserved</span><span class="p">)</span>
</code></pre></div><div class="highlight"><pre><code class="c language-c" data-lang="c"><span class="n">JNIEXPORT</span> <span class="n">jint</span> <span class="n">JNICALL</span> 
<span class="n">Agent_OnAttach</span><span class="p">(</span><span class="n">JavaVM</span><span class="o">*</span> <span class="n">vm</span><span class="p">,</span> <span class="kt">char</span> <span class="o">*</span><span class="n">options</span><span class="p">,</span> <span class="kt">void</span> <span class="o">*</span><span class="n">reserved</span><span class="p">)</span>
</code></pre></div>
<p>与agent关闭相关的函数是<code>Agent_OnUnload</code>，当agent要被关闭时，虚拟机会调用这个函数，函数原型为：</p>
<div class="highlight"><pre><code class="c language-c" data-lang="c"><span class="n">JNIEXPORT</span> <span class="kt">void</span> <span class="n">JNICALL</span> 
<span class="n">Agent_OnUnload</span><span class="p">(</span><span class="n">JavaVM</span> <span class="o">*</span><span class="n">vm</span><span class="p">)</span>
</code></pre></div>
<ul>
<li>程序基本框架</li>
</ul>

<p>主要的内容框架在<code>Agent_OnLoad</code>中编写：</p>

<p>1 获取jvm环境</p>
<div class="highlight"><pre><code class="c language-c" data-lang="c"><span class="cm">/* get env */</span>
<span class="n">jvmtiEnv</span> <span class="o">*</span><span class="n">jvmti</span> <span class="o">=</span> <span class="nb">NULL</span><span class="p">;</span>
<span class="n">jvmtiError</span> <span class="n">error</span><span class="p">;</span>

<span class="n">error</span> <span class="o">=</span> <span class="p">(</span><span class="o">*</span><span class="n">jvm</span><span class="p">)</span><span class="o">-&gt;</span><span class="n">GetEnv</span><span class="p">(</span><span class="n">jvm</span><span class="p">,</span> <span class="p">(</span><span class="kt">void</span> <span class="o">**</span><span class="p">)</span><span class="o">&amp;</span><span class="n">jvmti</span><span class="p">,</span> <span class="n">JVMTI_VERSION</span><span class="p">);</span>
<span class="k">if</span> <span class="p">(</span><span class="n">error</span> <span class="o">!=</span> <span class="n">JNI_OK</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">fprintf</span><span class="p">(</span><span class="n">stderr</span><span class="p">,</span> <span class="s">&quot;Couldn&#39;t get JVMTI environment&quot;</span><span class="p">);</span>
    <span class="k">return</span> <span class="n">JNI_ERR</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div>
<p>可以为同一个虚拟机指定多个agent，每个agent都有自己的环境，在指定agent行为前，首先要获取的就是环境信息，后面的操作都是针对这个环境的。另外，JVMTI中的函数都会返回错误代码，在调用函数后，需要检查返回值，以确定函数调用是否成功。不同的函数会返回不同类型的错误码，可自行参阅JVMTI的API。</p>

<p>另外，需要注意，JVMTI程序可以使用C/C++编写，两者在调用函数时略有不同，上面的例子是用C编写，gcc编译。如果你使用C++编写，<code>GetEnv</code>需要这样调用：</p>
<div class="highlight"><pre><code class="c++ language-c++" data-lang="c++"><span class="n">error</span> <span class="o">=</span> <span class="p">(</span><span class="n">jvm</span><span class="p">)</span><span class="o">-&gt;</span><span class="n">GetEnv</span><span class="p">(</span><span class="k">reinterpret_cast</span><span class="o">&lt;</span><span class="kt">void</span><span class="o">**&gt;</span><span class="p">(</span><span class="o">&amp;</span><span class="n">jvmti</span><span class="p">),</span> <span class="n">JVMTI_VERSION_1_1</span><span class="p">);</span>
</code></pre></div>
<p>其它函数依次类推。 </p>

<p>2 添加capabilities</p>

<p>JVMTI中有很多事件，每个事件都对对应一些Capabilities，如果你想为此事件编写函数，就要开启相应的Capabilities，例如，我们想对 <code>JVMTI_EVENT_SINGLE_STEP</code> 事件编写函数，可以查到，需要开启<code>can_generate_single_step_events</code>：</p>
<div class="highlight"><pre><code class="c language-c" data-lang="c"><span class="cm">/* add capabilities */</span><span class="p">]</span>
<span class="n">jvmtiCapabilities</span> <span class="n">capa</span><span class="p">;</span>
<span class="n">memset</span><span class="p">(</span><span class="o">&amp;</span><span class="n">capa</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">jvmtiCapabilities</span><span class="p">));</span>
<span class="n">capa</span><span class="p">.</span><span class="n">can_generate_single_step_events</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
<span class="n">error</span> <span class="o">=</span> <span class="p">(</span><span class="o">*</span><span class="n">jvmti</span><span class="p">)</span><span class="o">-&gt;</span><span class="n">AddCapabilities</span><span class="p">(</span><span class="n">jvmti</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">capa</span><span class="p">);</span>
<span class="n">check_jvmti_error</span><span class="p">(</span><span class="n">jvmti</span><span class="p">,</span> <span class="n">error</span><span class="p">,</span> \
        <span class="s">&quot;Unable to get necessary JVMTI capabilities.&quot;</span><span class="p">);</span>
</code></pre></div>
<p>如果开启的Capabilities多于一个，不用声明多个<code>jvmtiCapabilities</code>变量，只需要使用类似</p>
<div class="highlight"><pre><code class="c language-c" data-lang="c"><span class="n">capa</span><span class="p">.</span><span class="n">can_generate_single_step_events</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
</code></pre></div>
<p>的方式指定就行。</p>

<p>3 指定事件</p>

<p>JVMTI编写的目的是，当虚拟机中一个事件发生时，调用我们为此事件编写的函数。所以我们需要指定哪个事件发生时，通知agent：</p>
<div class="highlight"><pre><code class="c language-c" data-lang="c"><span class="cm">/* set events */</span>
<span class="n">error</span> <span class="o">=</span> <span class="p">(</span><span class="o">*</span><span class="n">jvmti</span><span class="p">)</span><span class="o">-&gt;</span><span class="n">SetEventNotificationMode</span> \
        <span class="p">(</span><span class="n">jvmti</span><span class="p">,</span> <span class="n">JVMTI_ENABLE</span><span class="p">,</span> <span class="n">JVMTI_EVENT_SINGLE_STEP</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">);</span>
<span class="n">check_jvmti_error</span><span class="p">(</span><span class="n">jvmti</span><span class="p">,</span> <span class="n">error</span><span class="p">,</span> <span class="s">&quot;Cannot set event notification&quot;</span><span class="p">);</span>
</code></pre></div>
<p>其中 <code>JVMTI_EVENT_SINGLE_STEP</code> 就是事件代码。</p>

<blockquote>
<p>需要特别注意的是，要先开启相关capabilities，然后才能指定事件。</p>
</blockquote>

<p>4 设置回调函数</p>

<p>我们还需要为事件指定回调函数，并自行编写回调函数，事件回调函数的接口是由JVMTI指定的，例如<code>JVMTI_EVENT_SINGLE_STEP</code>事件的回调函数原型：</p>
<div class="highlight"><pre><code class="text language-text" data-lang="text">void JNICALL
SingleStep(jvmtiEnv *jvmti_env,
            JNIEnv* jni_env,
            jthread thread,
            jmethodID method,
            jlocation location)
</code></pre></div>
<p>为事件指定回调函数的方法是：</p>
<div class="highlight"><pre><code class="c language-c" data-lang="c"><span class="n">jvmtiEventCallbacks</span> <span class="n">callbacks</span><span class="p">;</span>

<span class="cm">/* add callbacks */</span>
<span class="n">memset</span><span class="p">(</span><span class="o">&amp;</span><span class="n">callbacks</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">callbacks</span><span class="p">));</span>
<span class="n">callbacks</span><span class="p">.</span><span class="n">SingleStep</span> <span class="o">=</span> <span class="o">&amp;</span><span class="n">callbackSingleStep</span><span class="p">;</span>
<span class="n">error</span> <span class="o">=</span> <span class="p">(</span><span class="o">*</span><span class="n">jvmti</span><span class="p">)</span><span class="o">-&gt;</span><span class="n">SetEventCallbacks</span> \
        <span class="p">(</span><span class="n">jvmti</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">callbacks</span><span class="p">,</span> <span class="p">(</span><span class="n">jint</span><span class="p">)</span><span class="k">sizeof</span><span class="p">(</span><span class="n">callbacks</span><span class="p">));</span> 
<span class="n">check_jvmti_error</span><span class="p">(</span><span class="n">jvmti</span><span class="p">,</span> <span class="n">error</span><span class="p">,</span> <span class="s">&quot;Canot set jvmti callbacks&quot;</span><span class="p">);</span>
</code></pre></div>
<p>之后，我们需要自己编写 <code>callbackSingleStep</code>函数：</p>
<div class="highlight"><pre><code class="c language-c" data-lang="c"><span class="kt">void</span> <span class="n">JNICALL</span>
<span class="nf">callbackSingleStep</span><span class="p">(</span>
    <span class="n">jvmtiEnv</span> <span class="o">*</span><span class="n">jvmti</span><span class="p">,</span> 
    <span class="n">JNIEnv</span><span class="o">*</span> <span class="n">jni</span><span class="p">,</span> 
    <span class="n">jthread</span> <span class="kr">thread</span><span class="p">,</span>
    <span class="n">jmethodID</span> <span class="n">method</span><span class="p">,</span>
    <span class="n">jlocation</span> <span class="n">location</span><span class="p">)</span> <span class="p">{</span>

<span class="p">}</span>
</code></pre></div>
<h3>运行agent</h3>

<p>运行agent，通过指定虚拟机参数来设定，例如运行<code>PossibleReordering</code>时：</p>
<div class="highlight"><pre><code class="sh language-sh" data-lang="sh">java -classpath . <span class="se">\</span>
    -agentpath:<span class="sb">`</span><span class="nb">pwd</span><span class="sb">`</span>/jvmagent/TraceAgent.so PossibleReordering
</code></pre></div>
<p>其中<code>TraceAgent.so</code>就是编译后生成的agent。</p>

<h2>使用JVMTI获取多线程程序指令执行次序</h2>

<p>我们知道，在Java虚拟机中的运行时数据区中，每个线程都有它的私有区域，每个线程有自己的PC寄存器，PC寄存器表示线程当前执行的指令在内存中的地址。其实我最初的目的是想得到这个PC的值，但是找了很久都没有找到，然后在JVMTI中找到了类似的概念。</p>

<p>在JVMTI中，介绍单步事件(Single Step Event)时说，当一个线程到达一个新的位置(location)时，单步事件就会产生。单步事件使agent以虚拟机允许的最细粒度，跟踪线程执行。</p>

<p>我们回到单步事件回调函数的原型：</p>
<div class="highlight"><pre><code class="c language-c" data-lang="c"><span class="kt">void</span> <span class="n">JNICALL</span>
<span class="n">SingleStep</span><span class="p">(</span><span class="n">jvmtiEnv</span> <span class="o">*</span><span class="n">jvmti_env</span><span class="p">,</span>
            <span class="n">JNIEnv</span><span class="o">*</span> <span class="n">jni_env</span><span class="p">,</span>
            <span class="n">jthread</span> <span class="kr">thread</span><span class="p">,</span>
            <span class="n">jmethodID</span> <span class="n">method</span><span class="p">,</span>
            <span class="n">jlocation</span> <span class="n">location</span><span class="p">)</span>
</code></pre></div>
<p>其中的 <code>location</code> 就是新指令的位置。</p>

<p>我们首先来编写一个Java多线程程序，这个程序是 《Java并发编程实战》(Java Concurrency in Practice) 中的一个例子，我做了一点变形：</p>
<div class="highlight"><pre><code class="java language-java" data-lang="java"><span class="kn">import</span> <span class="nn">java.lang.Thread</span><span class="o">;</span>

<span class="kd">public</span> <span class="kd">class</span> <span class="nc">PossibleReordering</span> <span class="o">{</span>
    <span class="kd">static</span> <span class="kt">int</span> <span class="n">x</span> <span class="o">=</span> <span class="mi">0</span><span class="o">,</span> <span class="n">y</span> <span class="o">=</span> <span class="mi">0</span><span class="o">;</span>
    <span class="kd">static</span> <span class="kt">int</span> <span class="n">a</span> <span class="o">=</span> <span class="mi">0</span><span class="o">,</span> <span class="n">b</span> <span class="o">=</span> <span class="mi">0</span><span class="o">;</span>

    <span class="kd">public</span> <span class="kd">static</span> <span class="kt">void</span> <span class="nf">main</span><span class="o">(</span><span class="n">String</span><span class="o">[]</span> <span class="n">args</span><span class="o">)</span> <span class="kd">throws</span> <span class="n">InterruptedException</span> <span class="o">{</span>

        <span class="n">Thread</span> <span class="n">one</span> <span class="o">=</span> <span class="k">new</span> <span class="n">Thread</span><span class="o">(</span><span class="k">new</span> <span class="n">Runnable</span><span class="o">()</span> <span class="o">{</span>
            <span class="kd">public</span> <span class="kt">void</span> <span class="nf">run</span><span class="o">()</span> <span class="o">{</span>
                <span class="n">a</span> <span class="o">=</span> <span class="mi">1</span><span class="o">;</span>
                <span class="n">x</span> <span class="o">=</span> <span class="n">b</span><span class="o">;</span>
            <span class="o">}</span>
        <span class="o">});</span>

        <span class="n">Thread</span> <span class="n">other</span> <span class="o">=</span> <span class="k">new</span> <span class="n">Thread</span><span class="o">(</span><span class="k">new</span> <span class="n">Runnable</span><span class="o">()</span> <span class="o">{</span>
            <span class="kd">public</span> <span class="kt">void</span> <span class="nf">run</span><span class="o">()</span> <span class="o">{</span>
                <span class="n">b</span> <span class="o">=</span> <span class="mi">1</span><span class="o">;</span>
                <span class="n">y</span> <span class="o">=</span> <span class="n">a</span><span class="o">;</span>
                <span class="n">a</span> <span class="o">=</span> <span class="mi">1</span><span class="o">;</span>
                <span class="n">x</span> <span class="o">=</span> <span class="n">b</span><span class="o">;</span>
                <span class="n">a</span> <span class="o">=</span> <span class="mi">1</span><span class="o">;</span>
                <span class="n">x</span> <span class="o">=</span> <span class="n">b</span><span class="o">;</span>
                <span class="n">a</span> <span class="o">=</span> <span class="mi">1</span><span class="o">;</span>
                <span class="n">x</span> <span class="o">=</span> <span class="n">b</span><span class="o">;</span>
                <span class="n">a</span> <span class="o">=</span> <span class="mi">1</span><span class="o">;</span>
                <span class="n">x</span> <span class="o">=</span> <span class="n">b</span><span class="o">;</span>
                <span class="n">a</span> <span class="o">=</span> <span class="mi">1</span><span class="o">;</span>
                <span class="n">x</span> <span class="o">=</span> <span class="n">b</span><span class="o">;</span>
                <span class="n">a</span> <span class="o">=</span> <span class="mi">1</span><span class="o">;</span>
                <span class="n">x</span> <span class="o">=</span> <span class="n">b</span><span class="o">;</span>
                <span class="n">a</span> <span class="o">=</span> <span class="mi">1</span><span class="o">;</span>
                <span class="n">x</span> <span class="o">=</span> <span class="n">b</span><span class="o">;</span>
            <span class="o">}</span>
        <span class="o">});</span>


        <span class="n">one</span><span class="o">.</span><span class="na">start</span><span class="o">();</span>
        <span class="n">other</span><span class="o">.</span><span class="na">start</span><span class="o">();</span>
        <span class="n">one</span><span class="o">.</span><span class="na">join</span><span class="o">();</span>
        <span class="n">other</span><span class="o">.</span><span class="na">join</span><span class="o">();</span>
    <span class="o">}</span>
<span class="o">}</span>
</code></pre></div>
<p>给Thread other多加了一些语句，用以区分两个线程。</p>

<p>这里有一个问题是，我们关心的其实只是两个线程的 <code>run</code>函数中指令的次序，而单步事件会在任何指令执行时，都调用回调函数，这就需要我们在回调函数中，只保留源代码中的两个线程的<code>run</code>函数中的指令的位置，其它的都过滤掉。</p>

<p>我们可以使用JVMTI提供的 <code>GetMethodName</code> 来得到函数名，使用 <code>GetMethodDeclaringClass</code>得到类名，然后通过比较类名和函数名，只保留 <code>run</code>中的指令：</p>
<div class="highlight"><pre><code class="c language-c" data-lang="c"><span class="n">error</span> <span class="o">=</span> <span class="p">(</span><span class="o">*</span><span class="n">jvmti</span><span class="p">)</span><span class="o">-&gt;</span><span class="n">GetMethodName</span><span class="p">(</span> \
        <span class="n">jvmti</span><span class="p">,</span> <span class="n">method</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">method_name</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">method_signature</span><span class="p">,</span> <span class="n">SKIP_GENERIC</span><span class="p">);</span>

<span class="n">error</span> <span class="o">=</span> <span class="p">(</span><span class="o">*</span><span class="n">jvmti</span><span class="p">)</span><span class="o">-&gt;</span><span class="n">GetMethodDeclaringClass</span><span class="p">(</span> \
         <span class="n">jvmti</span><span class="p">,</span> <span class="n">method</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">declaring_class</span><span class="p">);</span>


<span class="k">if</span> <span class="p">(</span><span class="n">strncmp</span><span class="p">(</span><span class="n">method_name</span><span class="p">,</span> <span class="s">&quot;run&quot;</span><span class="p">,</span> <span class="mi">4</span><span class="p">)</span> <span class="o">==</span> <span class="mi">0</span>   <span class="o">&amp;&amp;</span> \
      <span class="n">strstr</span><span class="p">(</span><span class="n">class_signature</span><span class="p">,</span> <span class="s">&quot;PossibleReordering&quot;</span><span class="p">)</span> <span class="o">!=</span> <span class="nb">NULL</span><span class="p">)</span> <span class="p">{</span>
      <span class="n">printf</span><span class="p">(</span><span class="s">&quot;%s</span><span class="se">\t</span><span class="s">&quot;</span><span class="p">,</span> <span class="n">thread_info</span><span class="p">.</span><span class="n">name</span><span class="p">);</span>
      <span class="n">printf</span><span class="p">(</span><span class="s">&quot;%s</span><span class="se">\t</span><span class="s">&quot;</span><span class="p">,</span> <span class="n">class_signature</span><span class="p">);</span>
      <span class="n">printf</span><span class="p">(</span><span class="s">&quot;%lld</span><span class="se">\t</span><span class="s">&quot;</span><span class="p">,</span> <span class="n">location</span><span class="p">);</span>
      <span class="n">printf</span><span class="p">(</span><span class="s">&quot;%s %lld:%lld</span><span class="se">\t</span><span class="s">&quot;</span><span class="p">,</span> <span class="n">method_name</span><span class="p">,</span> <span class="n">s_location</span><span class="p">,</span> <span class="n">e_location</span><span class="p">);</span>
      <span class="n">printf</span><span class="p">(</span><span class="s">&quot;</span><span class="se">\n</span><span class="s">&quot;</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div>
<p>执行下列命令：</p>
<div class="highlight"><pre><code class="sh language-sh" data-lang="sh">java -classpath . -agentpath:<span class="sb">`</span><span class="nb">pwd</span><span class="sb">`</span>/jvmagent/TraceAgent.so<span class="o">=</span>log.txt PossibleReordering
</code></pre></div>
<p>即可得到指令次序信息：</p>
<div class="highlight"><pre><code class="text language-text" data-lang="text">Thread-0    LPossibleReordering$1;  0   run 0:10    
Thread-0    LPossibleReordering$1;  1   run 0:10    
Thread-1    LPossibleReordering$2;  0   run 0:80    
Thread-0    LPossibleReordering$1;  4   run 0:10    
Thread-0    LPossibleReordering$1;  7   run 0:10    
Thread-0    LPossibleReordering$1;  10  run 0:10    
Thread-1    LPossibleReordering$2;  1   run 0:80    
Thread-1    LPossibleReordering$2;  4   run 0:80    
Thread-1    LPossibleReordering$2;  7   run 0:80    
Thread-1    LPossibleReordering$2;  10  run 0:80    
Thread-1    LPossibleReordering$2;  11  run 0:80    
Thread-1    LPossibleReordering$2;  14  run 0:80    
Thread-1    LPossibleReordering$2;  17  run 0:80    
Thread-1    LPossibleReordering$2;  20  run 0:80    
Thread-1    LPossibleReordering$2;  21  run 0:80    
Thread-1    LPossibleReordering$2;  24  run 0:80    
Thread-1    LPossibleReordering$2;  27  run 0:80    
Thread-1    LPossibleReordering$2;  30  run 0:80    
Thread-1    LPossibleReordering$2;  31  run 0:80    
Thread-1    LPossibleReordering$2;  34  run 0:80    
Thread-1    LPossibleReordering$2;  37  run 0:80    
Thread-1    LPossibleReordering$2;  40  run 0:80    
Thread-1    LPossibleReordering$2;  41  run 0:80    
Thread-1    LPossibleReordering$2;  44  run 0:80    
Thread-1    LPossibleReordering$2;  47  run 0:80    
Thread-1    LPossibleReordering$2;  50  run 0:80    
Thread-1    LPossibleReordering$2;  51  run 0:80    
Thread-1    LPossibleReordering$2;  54  run 0:80    
Thread-1    LPossibleReordering$2;  57  run 0:80    
Thread-1    LPossibleReordering$2;  60  run 0:80    
Thread-1    LPossibleReordering$2;  61  run 0:80    
Thread-1    LPossibleReordering$2;  64  run 0:80    
Thread-1    LPossibleReordering$2;  67  run 0:80    
Thread-1    LPossibleReordering$2;  70  run 0:80    
Thread-1    LPossibleReordering$2;  71  run 0:80    
Thread-1    LPossibleReordering$2;  74  run 0:80    
Thread-1    LPossibleReordering$2;  77  run 0:80    
Thread-1    LPossibleReordering$2;  80  run 0:80        
</code></pre></div>
<p>最终的源代码中，我还输出了线程名，和方法的指令地址范围。</p>

<p>我们可以反编译 <code>PossibleReordering$1</code> 和 <code>PossibleReordering$2</code>，看看相应的指令范围是否可以对应上。</p>
<div class="highlight"><pre><code class="sh language-sh" data-lang="sh"><span class="nv">$ </span>javap -c PossibleReordering<span class="se">\$</span>1.class
Compiled from <span class="s2">&quot;PossibleReordering.java&quot;</span>
final class PossibleReordering<span class="nv">$1</span> implements java.lang.Runnable <span class="o">{</span>
  PossibleReordering<span class="nv">$1</span><span class="o">()</span>;
    Code:
       0: aload_0       
       1: invokespecial <span class="c">#1                  // Method java/lang/Object.&quot;&lt;init&gt;&quot;:()V</span>
       4: <span class="k">return        </span>

<span class="k">  </span>public void run<span class="o">()</span>;
    Code:
       0: iconst_1      
       1: putstatic     <span class="c">#2                  // Field PossibleReordering.a:I</span>
       4: getstatic     <span class="c">#3                  // Field PossibleReordering.b:I</span>
       7: putstatic     <span class="c">#4                  // Field PossibleReordering.x:I</span>
      10: <span class="k">return</span>        
<span class="o">}</span>
</code></pre></div><div class="highlight"><pre><code class="sh language-sh" data-lang="sh"><span class="nv">$ </span>javap -c PossibleReordering<span class="se">\$</span>2.class 
Compiled from <span class="s2">&quot;PossibleReordering.java&quot;</span>
final class PossibleReordering<span class="nv">$2</span> implements java.lang.Runnable <span class="o">{</span>
  PossibleReordering<span class="nv">$2</span><span class="o">()</span>;
    Code:
       0: aload_0       
       1: invokespecial <span class="c">#1                  // Method java/lang/Object.&quot;&lt;init&gt;&quot;:()V</span>
       4: <span class="k">return        </span>

<span class="k">  </span>public void run<span class="o">()</span>;
    Code:
       0: iconst_1      
       1: putstatic     <span class="c">#2                  // Field PossibleReordering.b:I</span>
       4: getstatic     <span class="c">#3                  // Field PossibleReordering.a:I</span>
       7: putstatic     <span class="c">#4                  // Field PossibleReordering.y:I</span>
      10: iconst_1      
      11: putstatic     <span class="c">#3                  // Field PossibleReordering.a:I</span>
      14: getstatic     <span class="c">#2                  // Field PossibleReordering.b:I</span>
      17: putstatic     <span class="c">#5                  // Field PossibleReordering.x:I</span>
      20: iconst_1      
      21: putstatic     <span class="c">#3                  // Field PossibleReordering.a:I</span>
      24: getstatic     <span class="c">#2                  // Field PossibleReordering.b:I</span>
      27: putstatic     <span class="c">#5                  // Field PossibleReordering.x:I</span>
      30: iconst_1      
      31: putstatic     <span class="c">#3                  // Field PossibleReordering.a:I</span>
      34: getstatic     <span class="c">#2                  // Field PossibleReordering.b:I</span>
      37: putstatic     <span class="c">#5                  // Field PossibleReordering.x:I</span>
      40: iconst_1      
      41: putstatic     <span class="c">#3                  // Field PossibleReordering.a:I</span>
      44: getstatic     <span class="c">#2                  // Field PossibleReordering.b:I</span>
      47: putstatic     <span class="c">#5                  // Field PossibleReordering.x:I</span>
      50: iconst_1      
      51: putstatic     <span class="c">#3                  // Field PossibleReordering.a:I</span>
      54: getstatic     <span class="c">#2                  // Field PossibleReordering.b:I</span>
      57: putstatic     <span class="c">#5                  // Field PossibleReordering.x:I</span>
      60: iconst_1      
      61: putstatic     <span class="c">#3                  // Field PossibleReordering.a:I</span>
      64: getstatic     <span class="c">#2                  // Field PossibleReordering.b:I</span>
      67: putstatic     <span class="c">#5                  // Field PossibleReordering.x:I</span>
      70: iconst_1      
      71: putstatic     <span class="c">#3                  // Field PossibleReordering.a:I</span>
      74: getstatic     <span class="c">#2                  // Field PossibleReordering.b:I</span>
      77: putstatic     <span class="c">#5                  // Field PossibleReordering.x:I</span>
      80: <span class="k">return</span>        
<span class="o">}</span>
</code></pre></div>
<p>可以看出，确实是一个线程的run方法指令范围是 <code>0:10</code> ，另一个是 <code>0:80</code>，说明我们正确获取了相应指令。</p>

<p>完整的源代码，包含如何编译，运行，可以在我的GitHub中找到：<a href="https://github.com/minixalpha/Demo/tree/master/AgentDemo">AgentDemo</a></p>


      </article>

      <div class="comments">
        
          <div id="disqus_thread"></div>
 <script type="text/javascript">
     /* * * CONFIGURATION VARIABLES: EDIT BEFORE PASTING INTO YOUR WEBPAGE * * */
     var disqus_shortname = 'minixalpha'; // required: replace example with your forum shortname

     /* * * DON'T EDIT BELOW THIS LINE * * */
     (function() {
         var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true;
         dsq.src = '//' + disqus_shortname + '.disqus.com/embed.js';
         (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq);
     })();
 </script>
 <noscript>Please enable JavaScript to view the <a href="http://disqus.com/?ref_noscript">comments powered by Disqus.</a></noscript>
    <a href="http://disqus.com" class="dsq-brlink">comments powered by <span class="logo-disqus">Disqus</span></a>
    

        
      </div>


      <footer>
        Copyright (c) minixalpha 2014
      </footer>

    </div>
  </body>
</html>
